
#include "client.h"

#include <boost/bind.hpp>

using boost::asio::ip::tcp;
using namespace std;

client::client(const std::string &server)
  : 
    LoggingService("CLIENT"),
    m_client_thread(0),
    m_io_service(),
    m_resolver(m_io_service),
    m_socket_to(m_io_service),
    m_socket_from(m_io_service),
    m_server(server),
    m_is_connected_to(false),
    m_is_connected_from(false),
    m_is_connected(false),
    m_is_authenticated(false),
    m_is_bidirectional(false)

{
//  boost::ref(this);
//  m_io_service.run();
  m_client_thread = & boost::thread(&client::thread_process, this);
}

client::~client()
{
  verbose() << "finishing" << endl;
  // do not interrupt the thread -- will finish itself :P
}

client::client(const client &c)
  :
    LoggingService("CLIENT"),
    m_io_service(),
    m_resolver(m_io_service),
    m_socket_to(m_io_service),
    m_socket_from(m_io_service),
    m_is_connected_to(false),
    m_is_connected_from(false),
    m_is_connected(false),
    m_is_authenticated(false),
    m_is_bidirectional(false)
{
  std::cout << "copying" << std::endl;
}


void client::thread_process()
{
  //try 
  {
    while (!boost::this_thread::interruption_requested())
    {
      assert(!m_is_connected_to && !m_is_connected_from);

      this->connect();

      try
      {
        m_io_service.run();

        fatal() << "*********************************************************" << endl;
        fatal() << "**            IO SERVICE RUN OUT OF WORK!!             **" << endl;
        fatal() << "*********************************************************" << endl;
        break;
      }
      catch (std::exception &e)
      {
        // ASSERT: client got disconnected
        error() << "error in m_io_service.run()" << e.what() << std::endl;
      }
      // will need to make sure we don't catch thread interruption
  //    catch (std::exception &e)
      /*
      catch (boost::thread_interrupted &e)
      {
        cout << "interrupted" << endl;
      }
      */

    }
    debug() << "thread interrupted, exiting" << endl;
  }
}


void client::connect()
{
  debug() << "attempting to connect" << endl;
  try
  {
    tcp::resolver::query query_to(tcp::v4(), m_server, "9790");
    tcp::resolver::query query_from(tcp::v4(), m_server, "9791");

 //   boost::asio::io_service::work work(m_io_service);

    m_resolver.async_resolve(
        query_to,
        boost::bind(
          &client::handle_resolve_to,
          this,
          boost::asio::placeholders::error,
          boost::asio::placeholders::iterator)
        );
    m_resolver.async_resolve(
        query_from,
        boost::bind(
          &client::handle_resolve_from,
          this,
          boost::asio::placeholders::error,
          boost::asio::placeholders::iterator)
        );


  }
  catch (std::exception &e)
  {
    error() << "Exception: " << e.what() << std::endl;
  }
  debug() << "connection jobs posted." << endl;
}


void client::handle_resolve_to(const boost::system::error_code &err, tcp::resolver::iterator iterator)
{
  if (!err)
  {
    verbose() << "address to resolution successful" << endl;

    tcp::endpoint endpoint = *iterator;
    m_socket_to.async_connect(endpoint, 
        boost::bind(
          &client::handle_connect_to, 
          this,
          boost::asio::placeholders::error, 
          ++iterator)
          );
  }
  else
  {
    fatal() << "handle resolve to: " << err.message() << endl;
  }

}

void client::handle_resolve_from(const boost::system::error_code &err, tcp::resolver::iterator iterator)
{
  if (!err)
  {
    verbose() << "address from resolution successful" << endl;

    tcp::endpoint endpoint = *iterator;
    m_socket_from.async_connect(endpoint, 
        boost::bind(
          &client::handle_connect_from, 
          this,
          boost::asio::placeholders::error, 
          ++iterator)
          );
  }
  else
  {
    fatal() << "handle resolve from: " << err.message() << endl;
  }

}
void client::handle_connect_to(const boost::system::error_code &err, boost::asio::ip::tcp::resolver::iterator iterator)
{
  if (!err)
  {
    info() << "connected to server" << endl;
     
    // async write?

  }
  else if (iterator != tcp::resolver::iterator())
  {
    debug() << "trying next endpoint" << endl;
    m_socket_to.close();
    tcp::endpoint endpoint = *iterator;
    m_socket_to.async_connect(endpoint, 
        boost::bind(
          &client::handle_connect_to, 
          this,
          boost::asio::placeholders::error, 
          ++iterator)
          );
  }
  else
  {
    fatal() << "handle connect to: " << err.message() << endl;
  }
}

void client::handle_connect_from(const boost::system::error_code &err, boost::asio::ip::tcp::resolver::iterator iterator)
{
  if (!err)
  {
    info() << "connected from server. entering listening" << endl;
     
    // async read?

    listen_from();
  }
  else if (iterator != tcp::resolver::iterator())
  {
    debug() << "trying next endpoint" << endl;
    m_socket_from.close();
    tcp::endpoint endpoint = *iterator;
    m_socket_from.async_connect(endpoint, 
        boost::bind(
          &client::handle_connect_from, 
          this,
          boost::asio::placeholders::error, 
          ++iterator)
          );
  }
  else
  {
    fatal() << "handle connect from: " << err.message() << endl;
  }

}

void client::listen_from()
{
  debug() << "client listens for commands from the server." << endl;
  boost::asio::async_read(
      m_socket_from, 
      boost::asio::buffer(m_buffer, COMMAND_LENGTH),  
      boost::bind(&client::handle_read_command_from, this, boost::asio::placeholders::error)
    );
}


void client::handle_read_command_from(const boost::system::error_code& err)
{
  if (!err)
  {
    info() << "received command from server: " << (int)(m_buffer[0]) << endl;
  }
  else
  {
    error() << "error reading command: " << err << endl;
  }

  
  listen_from();
}



void client::disconnect()
{

}



void client::login(const std::string &username, const std::string &password)
{
  m_username = username;
  m_password = password;

  login();
}

void client::login()
{

}

void client::operator ()()
{
  std::cout << "hello" << endl;

}
